package grpc_pool

import (
	"context"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

// 常量定义：默认配置参数（基于生产环境最佳实践）
const (
	// DialTimeout 创建连接的超时时间
	DialTimeout = 5 * time.Second
	// BackoffMaxDelay 连接失败后的最大退避延迟
	BackoffMaxDelay = 3 * time.Second
	// KeepAliveTime 客户端心跳发送间隔（检测连接活性）
	KeepAliveTime = 10 * time.Second
	// KeepAliveTimeout 心跳响应超时时间（超时则标记连接无效）
	KeepAliveTimeout = 3 * time.Second
	// CheckInterval 连接健康检测间隔（后台协程）
	CheckInterval = 30 * time.Second
	// InitialWindowSize HTTP/2 初始流窗口大小（1GB，提升大文件传输性能）
	InitialWindowSize = 1 << 30
	// InitialConnWindowSize HTTP/2 初始连接窗口大小（1GB）
	InitialConnWindowSize = 1 << 30
	// MaxSendMsgSize 最大发送消息大小（4GB）
	MaxSendMsgSize = 4 << 30
	// MaxRecvMsgSize 最大接收消息大小（4GB）
	MaxRecvMsgSize = 4 << 30
)

// Options 连接池配置选项
type Options struct {
	// Dial 创建 gRPC 连接的函数（必填，自定义连接参数）
	Dial func(address string) (*grpc.ClientConn, error)
	// MaxIdle 最小空闲连接数（连接池维持的基础连接数，避免频繁创建）
	MaxIdle int
	// MaxActive 最大活跃连接数（连接池允许的最大连接数，防止连接风暴）
	MaxActive int
	// MaxConcurrentStreams 单个连接支持的最大并发流数（与 HTTP/2 协议限制对齐）
	MaxConcurrentStreams int
	// Reuse 连接数达上限时是否复用已有连接（true=复用，false=创建临时连接）
	Reuse bool
	// HealthCheck 自定义健康检测函数（可选，默认使用 gRPC 连接状态）
	HealthCheck func(conn *grpc.ClientConn) bool
	// CheckInterval 健康检测间隔（可选，默认 30s）
	CheckInterval time.Duration
}

// DefaultOptions 默认配置（生产环境推荐值）
var DefaultOptions = Options{
	Dial:                 Dial,
	MaxIdle:              8,
	MaxActive:            64,
	MaxConcurrentStreams: 64,
	Reuse:                true,
	HealthCheck:          nil, // 使用默认健康检测（依赖 gRPC 连接状态）
	CheckInterval:        CheckInterval,
}

// Dial 默认的 gRPC 连接创建函数（基于最佳实践配置）
func Dial(address string) (*grpc.ClientConn, error) {
	ctx, cancel := context.WithTimeout(context.Background(), DialTimeout)
	defer cancel()

	// gRPC 连接配置：开启 keepalive、大窗口、消息大小限制
	return grpc.DialContext(
		ctx,
		address,
		grpc.WithInsecure(), // 生产环境建议替换为 TLS 配置（grpc.WithTransportCredentials）
		grpc.WithBackoffMaxDelay(BackoffMaxDelay),
		grpc.WithInitialWindowSize(InitialWindowSize),
		grpc.WithInitialConnWindowSize(InitialConnWindowSize),
		grpc.WithDefaultCallOptions(
			grpc.MaxCallSendMsgSize(MaxSendMsgSize),
			grpc.MaxCallRecvMsgSize(MaxRecvMsgSize),
		),
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time:                KeepAliveTime,
			Timeout:             KeepAliveTimeout,
			PermitWithoutStream: true, // 无活跃流时仍发送心跳
		}),
	)
}

// DialTest 测试用连接创建函数（简化配置，禁用部分性能优化）
func DialTest(address string) (*grpc.ClientConn, error) {
	ctx, cancel := context.WithTimeout(context.Background(), DialTimeout)
	defer cancel()

	return grpc.DialContext(ctx, address, grpc.WithInsecure())
}
